Conversation
Add full async preload download flow and harden progress/status callbacks. - Implement StartPackageDownload as an async method (with NoOptimization/NoInlining) that initializes the plugin COM, queries total and already-downloaded sizes, ensures disk space, starts the preload task and awaits it. Handles cancellation, exceptions, and updates Status/IsRunning and logging. - Implement IsPreloadCompleted to check preload completion by comparing downloaded vs total size via plugin calls (with error handling). - Add defensive try/catch around UpdateProgressCallback and UpdateStatusCallback to prevent unhandled exceptions from crossing reverse P/Invoke boundaries (which would cause FailFast). Mirror aggregate progress into per-file fields, compute speeds/time left safely, and log swallowed exceptions. - Update StartPackageVerification comment to note verification is included in preload, and relax an OperationCanceledException catch condition to always handle cancellations. - Add additional debug/error logging throughout to aid troubleshooting.
Register a native per-file progress callback for plugins and use it to populate per-file progress fields. - Introduces an unmanaged Cdecl delegate and GCHandle to hold a reverse P/Invoke callback (OnPerFileProgressCallback) and safe registration/unregistration helpers (TryRegisterPerFileProgressCallback, UnregisterPerFileProgressCallback). - Calls registration before starting preload/download and ensures unregistration in finally blocks. - OnPerFileProgressCallback updates per-file bytes/percentage inside a try/catch (must not throw when invoked from native AOT). - Adjusts progress reporting: when the plugin provides per-file updates, use that data (and only mirror speed from aggregate); otherwise fall back to mirroring aggregate values into per-file fields. - Adds PluginInfo.EnablePerFileProgressCallback and DisablePerFileProgressCallback which call the plugin export SetPerFileProgressCallback (Cdecl) and log failures.
100% reviewed source file: 'en_US.json' on 'de_DE'.
Adds @perfectdelusions as a contributor for translation. This was requested by bagusnl [in this comment](#866 (comment)) [skip ci]
100% reviewed source file: 'en_US.json' on 'ja_JP'.
100% reviewed source file: 'en_US.json' on 'ja_JP'.
# Main Goal
Implement robust plugin-based preload, install, and update workflows
with per-file progress tracking, proper exception safety across P/Invoke
boundaries, and full `IGameInstallManager` compliance.
This pull request introduces significant improvements to the
plugin-based game installation system, with a focus on adding robust
per-file progress tracking and enhancing exception safety across reverse
P/Invoke boundaries.
## PR Status :
- Overall Status : Done
- Commits : Done
- Synced to base (Collapse:main) : **Yes**
- Build status : **OK**
- Crashing : **No**
- Bug found caused by PR : 0
### Templates
<details>
<summary>Changelog Prefixes</summary>
```
**[New]**
**[Imp]**
**[Fix]**
**[Loc]**
**[Doc]**
```
</details>
**Key changes include:**
### Per-file Progress Tracking and Plugin Integration
- **[New]** Added support for per-file progress callbacks in
`PluginGameInstallWrapper`, enabling more granular progress reporting
during game installation and download. This includes defining a new
delegate, registering/unregistering the callback with the plugin, and
updating progress fields accordingly.
[[1]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3R32-R34)],
[[2]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3R68-R71)],
[[3]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3R112-R165)],
[[4]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3L199-R326)],
[[5]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3R360-R361)])
- **[New]** Implemented new methods in `PluginInfo` to register and
unregister the per-file progress callback with the plugin via exported
functions, with error handling and logging.
([[CollapseLauncher/Classes/Plugins/PluginInfo.csR332-R368](diffhunk://#diff-214962c07ee46ad27a3948675104d996ad7484aefe8ceec5b816b7cf9e5e9920R332-R368)])
- **[Imp]** Added guard in `TryRegisterPerFileProgressCallback` to
prevent leaking a GCHandle if the method is called more than once
without a matching unregister.
- **[Imp]** `Status.IsIncludePerFileIndicator` is now set to
`_hasPerFileProgress` after callback registration, so the UI correctly
shows or hides the per-file progress indicator based on plugin
capability.
### Exception Handling and Stability
- **[Imp]** Wrapped all reverse P/Invoke callbacks
(`UpdateProgressCallback`, `UpdateStatusCallback`, and the new per-file
progress callback) in try/catch blocks to prevent unhandled exceptions
from causing FailFast crashes, and added logging for swallowed
exceptions.
[[1]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3L265-R405)],
[[2]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3L298-R452)],
[[3]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3R467-R476)],
[[4]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3R495-R499)])
- **[Fix]** `StartPackageDownload` now rethrows general exceptions after
logging and Sentry reporting, preventing silent failures that would
cause the verify→install pipeline to proceed with incomplete files.
- **[Fix]** Added general `catch (Exception)` block to
`StartPackageInstallation` with Sentry reporting and rethrow, matching
`StartPackageDownload`'s error-handling pattern.
- **[Fix]** `IsPreloadCompleted` now propagates
`OperationCanceledException` instead of swallowing it as `false`, making
cancellation distinguishable from "not completed".
- **[Fix]** `Status.IsCompleted` is now only set to `true` on successful
completion in both `StartPackageDownload` and
`StartPackageInstallation`, tracked via a `bool isSuccess` flag.
Previously it was set unconditionally in `finally`, making failures
appear as completions.
### Download and Installation Workflow Improvements
- **[Imp]** Refactored `StartPackageDownload` and
`StartPackageInstallation` to integrate per-file progress
registration/unregistration and to provide detailed logging and status
updates throughout the process.
[[1]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3L199-R326)],
[[2]](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3R360-R361)])
- **[Imp]** Improved fallback logic for progress reporting when per-file
progress is not available, ensuring backward compatibility with older
plugins.
([[CollapseLauncher/Classes/Plugins/PluginGameInstallWrapper.csL298-R452](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3L298-R452)])
### Functional Enhancements
- **[New]** Implemented the `IsPreloadCompleted` method to accurately
check if a preload download is complete by querying the plugin for total
and downloaded sizes, with error handling.
([[CollapseLauncher/Classes/Plugins/PluginGameInstallWrapper.csL415-R598](diffhunk://#diff-6d567c471f08bddcc31eeaa19f72ae7f3b5ac31e87b270a3f617163d4abd22e3L415-R598)])
- **[New]** Implemented `UpdateCompletenessStatus` to manage
`IsRunning`/`IsCompleted`/`IsCanceled` state, progress indeterminate
flags, and Discord presence updates, matching the `InstallManagerBase`
convention.
- **[New]** Implemented `CleanUpGameFiles` to scan and remove residual
temp/staging files under `{GameDirPath}\TempPath\`, with support for the
`FileCleanupPage` dialog UI or direct deletion.
- **[Fix]** Fixed `Locale.Lang` → `Locale.Current.Lang` for all
localization string accesses in `UpdateStatusCallback`, since `Lang` is
an instance property on the `Locale` singleton.
### Dependency Updates
- Updated the `Microsoft.NET.ILLink.Tasks` NuGet package from version
10.0.4 to 10.0.5 across multiple projects.
[[1]](diffhunk://#diff-5bf3fa83f98fe8daf7c49a3e3d3527606da7a229c6c426e3f1c4589f9d5201a2L144-R146)],
[[2]](diffhunk://#diff-d995168d6cf0ddb519c9cefd49fc4b2c2dcb883b1e1659b235b2b7588cd9e174L44-R46)],
[[3]](diffhunk://#diff-1d488b2cd859ba970f407cfbd0b9fa172f3af4b1a552cfb0d1513703ee5eca10L23-R25)],
[[4]](diffhunk://#diff-380527d5b33abffbfdc0f985d1a4cc21db5a3a02d491e3d6faeb1a8221f91e78L7-R9)],
[[5]](diffhunk://#diff-e1e16457c4744398ddc7e200170ca02f199d9a91cfca304605360451fb395d99L7-R9)])
- Added `Microsoft.NETFramework.ReferenceAssemblies` version 1.0.3 as a
new dependency in `Hi3Helper.TaskScheduler`.
([[Hi3Helper.TaskScheduler/packages.lock.jsonR20-R28](diffhunk://#diff-f2a0d3ab578dcfda2cf3cd16b2233a855fcee0a591090a27c0b0dec5a540cdb1R20-R28)])
### Miscellaneous
- **[Loc]** Minor language improvement in the FFmpeg installation lock
message in `en_US.json`.
([[Hi3Helper.Core/Lang/en_US.jsonL563-R563](diffhunk://#diff-a3d62fecc34be7c1b6da28f60fa8ff816b5b7644582602d1888a759485f5a4e8L563-R563)])
+ Fix HomePage navigated on cancelling background activity while the game isn't displayed
| int previousStackLimit = LauncherFrame.CacheSize; | ||
| LauncherFrame.Navigate(pageType, null, transitionInfo ?? new DrillInNavigationTransitionInfo()); | ||
|
|
||
| if (isForceLoad) | ||
| { | ||
| LauncherFrame.BackStack.Clear(); | ||
| LauncherFrame.CacheSize = 0; | ||
| } |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| }; | ||
|
|
||
| NavigationViewControl.FooterMenuItems.Add(new NavigationViewItem { Icon = IconFilesCleanup, Tag = "filescleanup"}.BindNavigationViewItemText(Locale.Current, "Lang._FileCleanupPage.Title")); | ||
| NavigationViewControl.FooterMenuItems.Add<NavigationViewItem>("Lang._GameSettingsPage.PageTitle", "", gspPageType); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
Just realized that the filtering of the asset is still being redirected to FilterSophonPatchAssetList, which is unused rather than to FilterAssetList. This causes the supposedly deleted assets to get included, causing files to be redownloaded if not exist
+ Reduce check time for CG files as it should parse the CG category correctly
CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.Fetch.cs
Outdated
Show resolved
Hide resolved
This due to postproc being under GPL. We cannot use this part of the library.
This reverts commit 5c0e414.
+ Fix broken cutscene detection due to unmatching file size reported by metadata vs. the actual size reported by the URL (good job, HoYo. As always :]) + Dynamically loads Cache Update's unused file ignore list from preset config.
100% reviewed source file: 'en_US.json' on 'id_ID'.
100% reviewed source file: 'en_US.json' on 'zh_CN'.
| InstallProgressState.Removing => string.Format("Deleting" + ": " + Locale.Current.Lang._Misc.PerFromTo, _updateProgressProperty.StateCount, _updateProgressProperty.StateCountTotal), | ||
| InstallProgressState.Idle => Locale.Current.Lang._Misc.Idle, | ||
| InstallProgressState.Install => string.Format(Locale.Current.Lang._Misc.Extracting + ": " + Locale.Current.Lang._Misc.PerFromTo, _updateProgressProperty.StateCount, _updateProgressProperty.StateCountTotal), | ||
| InstallProgressState.Verify or InstallProgressState.Preparing => string.Format(Locale.Current.Lang._Misc.Verifying + ": " + Locale.Current.Lang._Misc.PerFromTo, _updateProgressProperty.StateCount, _updateProgressProperty.StateCountTotal), | ||
| _ => string.Format((!_updateProgressProperty.IsUpdateMode ? Locale.Current.Lang._Misc.Downloading : Locale.Current.Lang._Misc.Updating) + ": " + Locale.Current.Lang._Misc.PerFromTo, _updateProgressProperty.StateCount, _updateProgressProperty.StateCountTotal) | ||
| }; | ||
|
|
||
| Status.ActivityStatus = stateString; | ||
| Status.ActivityAll = string.Format(Locale.Current.Lang._Misc.PerFromTo, _updateProgressProperty.AssetCount, _updateProgressProperty.AssetCountTotal); |
There was a problem hiding this comment.
Bug: The UpdateStatusCallback method no longer uses null-conditional operators when accessing locale strings. This can cause a NullReferenceException that is silently caught, freezing the installation progress UI.
Severity: MEDIUM
Suggested Fix
Restore the null-conditional operators (?.) when accessing properties of Locale.Current.Lang._Misc within the UpdateStatusCallback method. This ensures that if locale data is null, the status string becomes null without throwing an exception, preventing the UI from freezing.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: CollapseLauncher/Classes/Plugins/PluginGameInstallWrapper.cs#L509-L517
Potential issue: In the `UpdateStatusCallback` method, accesses to
`Locale.Current.Lang._Misc` properties were changed to remove the null-conditional
operator (`?.`). If `Locale.Current.Lang` or `Locale.Current.Lang._Misc` is null, for
instance due to a locale loading failure, a `NullReferenceException` will be thrown.
While a `try/catch` block prevents a crash, it silently swallows the exception, which
stops `Status.ActivityStatus` from being updated. This results in the installation
progress UI freezing with stale status information instead of gracefully handling the
missing locale strings.
| if (asset.FT != FileType.Video || | ||
| string.IsNullOrEmpty(asset.RN) || | ||
| useFastCheck) | ||
| { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
Bug: The method TryIsAssetRemoteSizeEquals incorrectly returns true for non-video files with a size mismatch, causing the repair process to skip hash verification for corrupted assets.
Severity: HIGH
Suggested Fix
Modify the logic so that TryIsAssetRemoteSizeEquals is only called for video files. Alternatively, change TryIsAssetRemoteSizeEquals to return false for non-video files. This will ensure that files with a size mismatch proceed to the standard hash verification and repair flow.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.Check.Generic.cs#L71-L76
Potential issue: When a game asset has a size mismatch, the repair logic calls
`TryIsAssetRemoteSizeEquals`. For any non-video file, this method immediately returns
`true`. This causes the calling function, `IsHashMatchedAuto`, to incorrectly treat the
asset as valid and skip the required hash verification step. As a result, corrupted or
incomplete non-video game files that have a size mismatch will not be detected or
repaired by the system.
| string assetName = (gameLanguageType == AudioLanguageType.Japanese | ||
| ? entry.Value.PathJp | ||
| : entry.Value.PathCn) ?? throw new NullReferenceException(); |
There was a problem hiding this comment.
Bug: A NullReferenceException is thrown if a video asset path is null, which aborts the entire parallel asset retrieval process and returns an empty list.
Severity: MEDIUM
Suggested Fix
Instead of throwing an exception, gracefully handle null path entries. For example, use a continue statement or a null-conditional check to skip the problematic entry and log a warning, allowing the Parallel.ForEachAsync loop to process the remaining valid assets.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.AsbExt.Video.cs#L119-L121
Potential issue: In the `ImplCheckAndAdd` method, a null-coalescing operator is used to
throw a `NullReferenceException` if `entry.Value.PathJp` or `entry.Value.PathCn` is
null. This code runs inside a `Parallel.ForEachAsync` loop without a `try/catch` block.
If a null path is encountered, the exception will propagate and abort the entire video
asset retrieval process, causing the method to return an empty list. This prevents any
video assets from being processed if even one entry has a missing path.
Superseeding from #862
Preview 1.84.1 (Codename: Columbina)
Hewwo, it's neon-nyan here~
It's beeeeeen a while since the last 1.83.x update. For now, this release is focusing on more quality improvements, bug fixes and internal code reworks rather than new features as we are preparing on reworking Collapse for new codebase.
That being said, this 1.84 update will be the marked as the last V1 release after roughly 4 years since the start of this project as we are going to move into V2 codebase starting this year (at Q3 or Q4 2026). Thank you so much for your continous support and interest in this project💖
Without further ado, let's dig into what's new in this release so far.
What's new?
Reworked Background System
Since months, HoYoverse has updated HoYoPlay to support multiple background to display, including static image and dynamic background ones. This has been our backlog since this release as due to "spaghetti-code" nature of our entire codebase, this made us harder to adapt the changes and thus making Collapse still only support one static background image.
Thanks to this massive rework, we are now able to pull-off this feature by splitting the parts of the code into its own element, making it more easier and more manageable for the change and for incoming improvements.
We are also moving to FFmpeg as our secondary library for background video decoder if no built-in codec is present. You will be prompted to install the FFmpeg library if none of the required built-in Windows Media Foundation codec for VP9 or any codec is present.
2026-03-22.20-02-46.webm
The experience might still be sluggish due to rushed implementation. But this will be improved in future updates🤞
Update: 2026/03/27
Due to corrupted video background situation in any regions for Zenless Zone Zero game, FFmpeg has been set as a default decoder for Collapse Launcher now (You can opt-in for using built-in Windows Media Foundation decoder, though). You might be asked to install a new one if you don't have any defined in your system's Environment Variable.
2026-03-27.11-55-13.webm
Reworked Localization System #861
This is more like development-experience improvement rather than user focused ones. Previously in order to implement the localization for new elements, we have to manually map each class properties to represented JSON entries. Thanks to newly source-generated class mapper, every JSON entries will be mapped automatically. Each class properties can now be bind into UI element directly, making the UI able to update the visual itself rather than being told manually, one-by-one (which is expensive).
Reworked Download Speed Limiter #859
The feature has been long broken since last 1.83.x release due to inconsistency and changes to other download-related libraries. Even though if you're enabling this feature, you might experience that the download speed "isn't actually being limited" and noticing that your bandwidth is still being fully utilized. This feature should now be fully fixed by decentralizing the code of the feature into its own library and making it easier to maintain.
This feature could also be applied for any game plugins whose have v1-update4 API standard fully implemented.
Minor UI Adjustments
Not so noticeable UI changes at all. But it's worth to mention here.
1. News Carousel Design
2. About Card
Other New Changes
This caused by the filtering of the asset is still being redirected to FilterSophonPatchAssetList, which is unused rather than to FilterAssetList. This causes the supposedly deleted assets to get included, causing files to be redownloaded if not exist.
PR Status :
[Regression][High Priority] Game Config's API is not changed while changing the games (Assignee: @neon-nyan)Fixed as per 052cb94[High Priority] Fix deleted assets still getting downloaded while updating Honkai: Star Rail and Zenless Zone Zero (Assignee: @neon-nyan)Fixed as per f87ef76[High Priority] Adjust Video Assets metadata parser for Honkai Impact 3rd v8.8 (Assignee: @neon-nyan)Fixed as per bd26158Templates
Changelog Prefixes